#pragma once

#include "SideUserObject.h"
#include "RandomInterface.h"
#include "SideElement.h"
#include <vector>
#include <set>

#include "UserDefinedMesh.h"
#include "UserDefinedElem.h"
#include "UserDefinedSideElem.h"

using std::vector;
using std::set;
using std::string;

class RayLine;
class MCRadiationHeatFluxUserObject :
public SideUserObject,
public RandomInterface
{
public:
	MCRadiationHeatFluxUserObject(const InputParameters & parameters);
	Real getTemBar(int i) const {return _temperature_pow4_bar[i];}
	Real getRadiationFlux(int i) const { return _flux_radiation[i]; }
	Real getRadiationFlux(const Elem * elem) const { return _flux_radiation[Find_i(elem)]; }
	Real getRadiationFluxJacobi(const Elem * elem)  const  {return _flux_radiation_jacobi[Find_i(elem)];}
	Real getEpsilon(const Elem * elem)  const  {return _all_element[Find_i(elem)]->_absorptivity;}
	vector<Real> getRadiationFluxList() const { return _flux_radiation; }
	vector<Real> getRadiationFluxJacobiList() const { return _flux_radiation_jacobi; }
	vector<Point> getCenterPointList() const { return _center_point_list; }
	vector<Real> getEpsilonList() const { return _epsilon_list; }

	~MCRadiationHeatFluxUserObject();

protected :
	virtual void initialSetup();
	virtual void initialize();
	virtual void finalize();
	virtual void execute();
	virtual void threadJoin(const UserObject & uo){};

	virtual void computeRadiationFlux();
	void computeRD(int local_particle_count, bool have_medium);
	void ompComputeRD(int n_threads, bool have_medium);
	void Deal_with_BSMC(int BSMC);
	int Which_SideelementIntersectedByLine(RayLine& ray, SideElement * sideelement_i, vector<SideElement*> sideelement_vec, Point & point);
	int Find_j_of_RDij(SideElement * sideelement_i, vector<SideElement*> sideelement_vec, bool have_medium);
	int Find_i(const Elem * elem) const;
	int sideIntersectedByLine(const Elem * elem, int not_side, RayLine & ray_line, Point & intersection_point);
	int sideNeighborIsOn(const UserDefinedElem * elem, UserDefinedElem * neighbor);
	int pointInWhichSide(const Elem * elem, Point & point);
	int findFinalSideId(RayLine & ray_line, Point & point, SideElement * sideelement);
	bool ElemHaveNeighborInBlock(const Elem * elem, set<SubdomainID> block_ids);
	bool ElemInBlock(SubdomainID curr_SubdomainID, set<SubdomainID> block_ids);

	void print_mesh_to_file();
	void print_T_to_file();
	void print_flux_to_file();

	vector<SideElement*> _all_element;

	int _max_reflect_count;
	int _particle_count;
	vector<Real> _transmissivity;
	vector<Real> _absorptivity;
	vector<Real> _diffuse_reflectivity;
	vector<Real> _mirrors_reflectivity;
	bool _have_medium;
	string _filename;
	bool _mpi_or_omp;
	int _n_threads;
	int _BSMC;
	bool _write_or_read;

	set<SubdomainID> _block_ids;
	
	const VariableValue &_temperature;
	vector<Real> _temperature_pow4_bar;
	vector<Real> _temperature_pow3_bar;
	vector<Real> _temperature_bar;
	vector<Real> _flux_radiation;
	vector<Real> _flux_radiation_jacobi;
	vector<Real> _epsilon_list;
	vector<Point> _center_point_list;

};
template<> InputParameters validParams<MCRadiationHeatFluxUserObject>();
